home *** CD-ROM | disk | FTP | other *** search
/ X User Tools / X User Tools (O'Reilly and Associates)(1994).ISO / sources / xman / scllbylc < prev    next >
Text File  |  1994-09-27  |  33KB  |  1,229 lines

  1. /*
  2.  * xman - X window system manual page display program.
  3.  *
  4.  * $XConsortium: ScrollByL.c,v 1.27 91/07/26 18:30:47 dave Exp $
  5.  * $Header: ScrollByL.c,v 1.27 91/07/26 18:30:47 dave Exp $
  6.  *
  7.  * Copyright 1987, 1988 Massachusetts Institute of Technology
  8.  *
  9.  * Permission to use, copy, modify, and distribute this software and its
  10.  * documentation for any purpose and without fee is hereby granted, provided
  11.  * that the above copyright notice appear in all copies and that both that
  12.  * copyright notice and this permission notice appear in supporting
  13.  * documentation, and that the name of M.I.T. not be used in advertising or
  14.  * publicity pertaining to distribution of the software without specific,
  15.  * written prior permission.  M.I.T. makes no representations about the
  16.  * suitability of this software for any purpose.  It is provided "as is"
  17.  * without express or implied warranty.
  18.  *
  19.  * Author:    Chris D. Peterson, MIT Project Athena
  20.  * Created:   December 5, 1987
  21.  */
  22.  
  23. #if ( !defined(lint) && !defined(SABER))
  24.   static char rcs_version[] = "$Athena: ScrollByL.c,v 4.5 88/12/19 13:46:04 kit Exp $";
  25. #endif
  26.  
  27. #include <stdio.h>
  28. #include <ctype.h>
  29.  
  30. #include <X11/IntrinsicP.h>
  31. #include <sys/stat.h>        /* depends on IntrinsicP.h */
  32. #include <X11/StringDefs.h>
  33.  
  34. #include <X11/Xaw/Scrollbar.h>
  35. #include <X11/Xmu/Misc.h>
  36.  
  37. #include "ScrollByLP.h"
  38.  
  39. /* Default Translation Table */
  40.  
  41. static char defaultTranslations[] = 
  42.   "<Key>f:      Page(Forward) \n\
  43.    <Key>b:      Page(Back) \n\
  44.    <Key>1:      Page(Line, 1) \n\
  45.    <Key>2:      Page(Line, 2) \n\
  46.    <Key>3:      Page(Line, 3) \n\
  47.    <Key>4:      Page(Line, 4) \n\
  48.    <Key>\\ :    Page(Forward)";
  49.  
  50.       
  51. /****************************************************************
  52.  *
  53.  * ScrollByLine Resources
  54.  *
  55.  ****************************************************************/
  56.  
  57. #define Offset(field) XtOffset(ScrollByLineWidget, scroll.field)
  58. #define CoreOffset(field) XtOffset(ScrollByLineWidget, core.field)
  59.  
  60. static XtResource resources[] = {
  61.     {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
  62.        CoreOffset(width), XtRImmediate, (caddr_t) 500},
  63.     {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
  64.        CoreOffset(height), XtRImmediate, (caddr_t) 700},
  65.  
  66.     {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
  67.        Offset(foreground), XtRString, "XtDefaultForeground"},
  68.     {XtNforceVert, XtCBoolean, XtRBoolean, sizeof(Boolean),
  69.        Offset(force_vert), XtRImmediate, (caddr_t) FALSE},
  70.     {XtNindent, XtCIndent, XtRDimension, sizeof(Dimension),
  71.        Offset(indent), XtRImmediate, (caddr_t) 15},
  72.     {XtNuseRight, XtCBoolean, XtRBoolean, sizeof(Boolean),
  73.        Offset(use_right), XtRImmediate, (caddr_t) FALSE},
  74.     {XtNmanualFontNormal, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  75.        Offset(normal_font), XtRString, MANPAGE_NORMAL},
  76.     {XtNmanualFontBold, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  77.        Offset(bold_font), XtRString, MANPAGE_BOLD},
  78.     {XtNmanualFontItalic, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  79.        Offset(italic_font), XtRString, MANPAGE_ITALIC},
  80.     {XtNmanualFontSymbol, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
  81.        Offset(symbol_font), XtRString, MANPAGE_SYMBOL},
  82.     {XtNfile, XtCFile, XtRFile, sizeof(FILE *),
  83.        Offset(file), XtRImmediate, (caddr_t) NULL},
  84. };
  85.  
  86. #undef Offset
  87. #undef CoreOffset
  88.  
  89. /****************************************************************
  90.  *
  91.  * Full class record constant
  92.  *
  93.  ****************************************************************/
  94.  
  95. static Boolean ScrollVerticalText();
  96. static void MoveAndClearText(), LoadFile(), PrintText(), VerticalJump();
  97. static void VerticalScroll(), SetThumbHeight(), PaintText(), Layout();
  98.  
  99. /* semi - public functions. */
  100.  
  101. static void Realize(), Initialize(), Destroy(), Redisplay(), Page();
  102. static Boolean SetValuesHook();
  103.  
  104. static XtActionsRec actions[] = {
  105.   { "Page",   Page},
  106. };
  107.  
  108. #define superclass        (&simpleClassRec)
  109.  
  110. ScrollByLineClassRec scrollByLineClassRec = {
  111.   {
  112. /* core_class fields      */
  113.     /* superclass         */    (WidgetClass) superclass,
  114.     /* class_name         */    "ScrollByLine",
  115.     /* widget_size        */    sizeof(ScrollByLineRec),
  116.     /* class_initialize   */    NULL,
  117.     /* class_part_init    */    NULL,
  118.     /* class_inited       */    FALSE,
  119.     /* initialize         */    Initialize,
  120.     /* initialize_hook    */    NULL,
  121.     /* realize            */    Realize,
  122.     /* actions            */    actions,
  123.     /* num_actions      */    XtNumber(actions),
  124.     /* resources          */    resources,
  125.     /* num_resources      */    XtNumber(resources),
  126.     /* xrm_class          */    NULLQUARK,
  127.     /* compress_motion      */    TRUE,
  128.     /* compress_exposure  */    FALSE,
  129.     /* compress_enterleave*/    TRUE,
  130.     /* visible_interest   */    FALSE,
  131.     /* destroy            */    Destroy,
  132.     /* resize             */    Layout,
  133.     /* expose             */    Redisplay,
  134.     /* set_values         */    NULL,
  135.     /* set_values_hook    */    SetValuesHook,
  136.     /* set_values_almost  */    XtInheritSetValuesAlmost,
  137.     /* get_values_hook    */    NULL,
  138.     /* accept_focus       */    NULL,
  139.     /* version            */    XtVersion,
  140.     /* callback_private   */    NULL,
  141.     /* tm_table           */    defaultTranslations,
  142.     /* query_geometry      */    XtInheritQueryGeometry,
  143.     /* display_accelerator*/    XtInheritDisplayAccelerator,
  144.     /* extension      */    NULL,
  145.   },
  146.   { /* simple fields */
  147.     /* change_sensitive        */    XtInheritChangeSensitive
  148.   }
  149. };
  150.  
  151. WidgetClass scrollByLineWidgetClass = 
  152.             (WidgetClass) &scrollByLineClassRec;
  153.  
  154.  
  155. /****************************************************************
  156.  *
  157.  * Private Routines
  158.  *
  159.  ****************************************************************/
  160.  
  161. /*    Function Name: Layout
  162.  *    Description: This function lays out the scroll widget.
  163.  *    Arguments: w - the scroll widget.
  164.  *                 key - a boolean: if true then resize the widget to the child
  165.  *                                  if false the resize children to fit widget.
  166.  *    Returns: TRUE if successful.
  167.  */
  168.  
  169. static void
  170. Layout(w)
  171. Widget w;
  172. {    
  173.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  174.   Dimension width, height;
  175.   Widget bar;
  176.   Position bar_bw;
  177.  
  178. /* 
  179.  * For now always show the bar.
  180.  */
  181.  
  182.   bar = sblw->scroll.bar;
  183.   height = sblw->core.height;
  184.   width = sblw->core.width;
  185.   bar_bw = bar->core.border_width;
  186.  
  187.   /* Move child and v_bar to correct location. */
  188.  
  189.   if (sblw->scroll.use_right) {
  190.     XtMoveWidget(bar, width - (bar->core.width + bar_bw), - bar_bw);
  191.     sblw->scroll.offset = 0;
  192.   }
  193.   else {
  194.     XtMoveWidget(bar, - bar_bw, - bar_bw);
  195.     sblw->scroll.offset = bar->core.width + bar_bw;
  196.   }
  197.  
  198.   /* resize the scrollbar to be the correct height or width. */
  199.  
  200.   XtResizeWidget(bar, bar->core.width, height, bar->core.border_width);
  201.  
  202.   SetThumbHeight(w);
  203. }
  204.  
  205. /* ARGSUSED */
  206. static void 
  207. GExpose(w,junk,event,cont)
  208. Widget w;
  209. XtPointer junk;
  210. XEvent *event;
  211. Boolean *cont;
  212. {
  213.  
  214. /*
  215.  * Graphics exposure events are not currently sent to exposure proc.
  216.  */
  217.  
  218.   if (event->type == GraphicsExpose)
  219.     Redisplay(w, event, NULL);
  220.  
  221. } /* ChildExpose */
  222.  
  223. /*
  224.  * Repaint the widget's child Window Widget.
  225.  */
  226.  
  227. /* ARGSUSED */
  228. static void Redisplay(w, event, region)
  229. Widget w;
  230. XEvent *event;
  231. Region region;
  232. {
  233.   int top, height;        /* the locations of the top and height
  234.                    of the region that needs to be repainted. */
  235.   
  236. /*
  237.  * This routine tells the client which sections of the window to 
  238.  * repaint in his callback function which does the actual repainting.
  239.  */
  240.  
  241.   if (event->type == Expose) {
  242.     top = event->xexpose.y;
  243.     height = event->xexpose.height;
  244.   }
  245.   else {
  246.     top = event->xgraphicsexpose.y;
  247.     height  = event->xgraphicsexpose.height;
  248.   }
  249.   
  250.   PaintText(w, top, height);
  251. } /* redisplay (expose) */
  252.  
  253. /*    Function Name: PaintText
  254.  *    Description: paints the text at the give location for a given height.
  255.  *    Arguments: w - the sbl widget.
  256.  *                 y_loc, height - location and size of area to paint.
  257.  *    Returns: none
  258.  */
  259.  
  260. static void
  261. PaintText(w, y_loc, height)
  262. Widget w;
  263. int y_loc, height;
  264. {
  265.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  266.   int start_line, num_lines, location;
  267.  
  268.   start_line = y_loc / sblw->scroll.font_height + sblw->scroll.line_pointer;
  269.  
  270.   if (start_line >= sblw->scroll.lines)
  271.     return;
  272.   
  273.   num_lines = height / sblw->scroll.font_height + 1;
  274.  
  275. /*
  276.  * Only integer arithmetic makes this possible. 
  277.  */
  278.  
  279.   location =  y_loc / sblw->scroll.font_height * sblw->scroll.font_height;
  280.  
  281.   PrintText(w, start_line, num_lines, location);
  282.  
  283. /*    Function Name: Page
  284.  *    Description: This function pages the widget, by the amount it recieves
  285.  *                   from the translation Manager.
  286.  *    Arguments: w - the ScrollByLineWidget.
  287.  *                 event - the event that caused this return.
  288.  *                 params - the parameters passed to it.
  289.  *                 num_params - the number of parameters.
  290.  *    Returns: none.
  291.  */
  292.  
  293. /* ARGSUSED */
  294. static void 
  295. Page(w, event, params, num_params)
  296. Widget w;
  297. XEvent * event;
  298. String * params;
  299. Cardinal *num_params;
  300. {
  301.    ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  302.    Widget bar = sblw->scroll.bar;
  303.  
  304.    if (*num_params < 1)
  305.      return;
  306. /*
  307.  * If no scroll bar is visible then do not page, as the entire window is shown,
  308.  * of scrolling has been turned off. 
  309.  */
  310.  
  311.    if (bar == (Widget) NULL)
  312.      return;
  313.  
  314.    switch ( params[0][0] ) {
  315.    case 'f':
  316.    case 'F':
  317.      /* move one page forward */
  318.      VerticalScroll(bar, NULL, (int) bar->core.height);
  319.      break;
  320.    case 'b':
  321.    case 'B':
  322.      /* move one page backward */
  323.      VerticalScroll(bar, NULL,  - (int) bar->core.height);
  324.      break;
  325.    case 'L':
  326.    case 'l':
  327.      /* move one line forward */
  328.      VerticalScroll(bar, NULL, 
  329.             (int) atoi(params[1]) * sblw->scroll.font_height);
  330.      break;
  331.    default:
  332.      return;
  333.    }
  334. }
  335.  
  336. /*    Function Name: CreateScrollbar
  337.  *    Description: createst the scrollbar for us.
  338.  *    Arguments: w - sblw widget.
  339.  *    Returns: none.
  340.  */
  341.  
  342. static void
  343. CreateScrollbar(w)
  344. Widget w;
  345. {
  346.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  347.   Arg args[5];
  348.   Cardinal num_args = 0;
  349.  
  350.   if (sblw->scroll.bar != NULL) 
  351.     return;
  352.  
  353.   XtSetArg(args[num_args], XtNorientation, XtorientVertical); num_args++;
  354.   
  355.   sblw->scroll.bar = XtCreateWidget("scrollbar", scrollbarWidgetClass, w, 
  356.                     args, num_args);
  357.   XtAddCallback(sblw->scroll.bar, XtNscrollProc, VerticalScroll, NULL);
  358.   XtAddCallback(sblw->scroll.bar, XtNjumpProc, VerticalJump, NULL); 
  359. }
  360.  
  361. /*    Function Name: ScrollVerticalText
  362.  *    Description: This accomplished the actual movement of the text.
  363.  *    Arguments: w - the ScrollByLine Widget.
  364.  *                 new_line - the new location for the line pointer
  365.  *                 force_redisplay - should we force this window to get 
  366.  *                                   redisplayed?
  367.  *    Returns: True if the thumb needs to be moved.
  368.  */
  369.  
  370. static Boolean
  371. ScrollVerticalText(w, new_line, force_redisp)
  372. Widget w;
  373. int new_line;
  374. Boolean force_redisp;
  375. {
  376.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  377.   int num_lines = (int)w->core.height / sblw->scroll.font_height + 1;
  378.   int max_lines, old_line;
  379.   Boolean move_thumb = FALSE;
  380.  
  381. /*
  382.  * Do not let the window extend out of bounds.
  383.  */
  384.  
  385.   if ( new_line < 0) {
  386.     new_line = 0;
  387.     move_thumb = TRUE;
  388.   }
  389.   else {
  390.     max_lines = sblw->scroll.lines - (int)w->core.height / sblw->scroll.font_height;
  391.     AssignMax(max_lines, 0);
  392.  
  393.     if ( new_line > max_lines ) {
  394.       new_line = max_lines;
  395.       move_thumb = TRUE;
  396.     }
  397.   }
  398.  
  399. /* 
  400.  * If forced to redisplay then do a full redisplay and return.
  401.  */
  402.  
  403.   old_line = sblw->scroll.line_pointer;    
  404.   sblw->scroll.line_pointer = new_line;    /* Set current top of page. */
  405.  
  406.   if (force_redisp) 
  407.     MoveAndClearText(w, 0, /* cause a full redisplay */ 0, 0);
  408.  
  409.   if (new_line == old_line)
  410.     return(move_thumb);
  411.  
  412. /*
  413.  * Scroll forward.
  414.  */
  415.  
  416.   else if (new_line < old_line) {
  417.     int lines_to_scroll = old_line - new_line;
  418.     MoveAndClearText(w, 0, num_lines - lines_to_scroll, lines_to_scroll);
  419.   }
  420.  
  421. /* 
  422.  * Scroll back.
  423.  */
  424.  
  425.   else {
  426.     int lines_to_scroll = new_line - old_line;
  427.     MoveAndClearText(w, lines_to_scroll, num_lines - lines_to_scroll, 0);
  428.   }
  429.  
  430.   return(move_thumb);
  431. }
  432.  
  433. /*    Function Name: MoveAndClearText
  434.  *    Description: Blits as much text as it can and clear the
  435.  *                   remaining area with generate exposures TRUE.
  436.  *    Arguments: w - the sbl widget.
  437.  *                 old_y - the old y position.
  438.  *                 height - height of area to move.
  439.  *                 new_y - new y position.
  440.  *    Returns: none
  441.  */
  442.        
  443. static void
  444. MoveAndClearText(w, old_y, height, new_y)
  445. Widget w;
  446. int old_y, new_y, height;
  447. {
  448.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  449.   int from_left = sblw->scroll.indent + sblw->scroll.offset;
  450.   int y_clear;
  451.  
  452.   old_y *= sblw->scroll.font_height;
  453.   new_y *= sblw->scroll.font_height;
  454.   height *= sblw->scroll.font_height;
  455.  
  456. /*
  457.  * If we are already at the right location then do nothing.
  458.  * (height == 0).
  459.  *
  460.  * If we have scrolled more than a screen height then just clear
  461.  * the window.
  462.  */
  463.  
  464.   if (height <= sblw->scroll.font_height) { /* avoid rounding errors. */
  465.     XClearArea( XtDisplay(w), XtWindow(w), from_left, 0, 
  466.            (unsigned int) 0, (unsigned int) 0, FALSE);    
  467.     PaintText(w, 0, (int) sblw->core.height);
  468.     return;
  469.   }
  470.  
  471.   if ((int)height + (int)old_y > (int)w->core.height)
  472.     height = w->core.height - old_y;
  473.  
  474.   XCopyArea(XtDisplay(w), XtWindow(w), XtWindow(w), sblw->scroll.move_gc,
  475.         from_left, old_y, 
  476.         (unsigned int) w->core.width - from_left, (unsigned int) height,
  477.         from_left, new_y);
  478.  
  479.   height -= sblw->scroll.font_height/2;    /* clear 1/2 font of extra space,
  480.                        to make sure we don't lose or
  481.                        gain decenders. */
  482.   if (old_y > new_y)
  483.     y_clear = height;
  484.   else
  485.     y_clear = 0;
  486.   
  487. /*
  488.  * We cannot use generate exposures, since that may allow another move and
  489.  * clear before the area get repainted, this would be bad.
  490.  */
  491.  
  492.   XClearArea( XtDisplay(w), XtWindow(w), from_left, y_clear,
  493.          (unsigned int) 0, (unsigned int) (w->core.height - height),
  494.          FALSE);
  495.   PaintText(w, (int) y_clear, (int) (w->core.height - height));
  496. }
  497.  
  498. /*    Function Name: SetThumbHeight
  499.  *    Description: Set the height of the thumb.
  500.  *    Arguments: w - the sblw widget.
  501.  *    Returns: none
  502.  */
  503.  
  504. static void
  505. SetThumbHeight(w)
  506. Widget w;
  507. {
  508.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  509.   float shown;
  510.  
  511.   if (sblw->scroll.bar == NULL)
  512.     return;
  513.  
  514.   if (sblw->scroll.lines == 0) 
  515.     shown = 1.0;
  516.   else
  517.     shown = (float) w->core.height / (float) (sblw->scroll.lines *
  518.                           sblw->scroll.font_height);
  519.   if (shown > 1.0)
  520.     shown = 1.0;
  521.  
  522.   XawScrollbarSetThumb( sblw->scroll.bar, (float) -1, shown );
  523. }
  524.  
  525. /*    Function Name: SetThumb
  526.  *    Description: Set the thumb location.
  527.  *    Arguments: w - the sblw.
  528.  *    Returns: none
  529.  */
  530.  
  531. static void
  532. SetThumb(w) 
  533. Widget w;
  534. {
  535.   float location;
  536.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  537.  
  538.   if ( (sblw->scroll.lines == 0) || (sblw->scroll.bar == NULL) )
  539.     return;
  540.  
  541.   location = (float) sblw->scroll.line_pointer / (float) sblw->scroll.lines; 
  542.   XawScrollbarSetThumb( sblw->scroll.bar, location , (float) -1 );
  543. }
  544.  
  545. /*    Function Name: VerticalJump.
  546.  *    Description: This function moves the test
  547.  *                   as the vertical scroll bar is moved.
  548.  *    Arguments: w - the scrollbar widget.
  549.  *                 junk - not used.
  550.  *                 percent - the position of the scrollbar.
  551.  *    Returns: none.
  552.  */
  553.  
  554. /* ARGSUSED */
  555. static void
  556. VerticalJump(w, junk, percent_ptr)
  557. Widget w;
  558. caddr_t junk;
  559. caddr_t percent_ptr;
  560. {
  561.   float percent = *((float *) percent_ptr);
  562.   int new_line;            /* The new location for the line pointer. */
  563.   ScrollByLineWidget sblw = (ScrollByLineWidget) XtParent(w);
  564.  
  565.   new_line = (int) ((float) sblw->scroll.lines * percent);
  566.   if (ScrollVerticalText( (Widget) sblw, new_line, FALSE))
  567.     SetThumb((Widget) sblw);
  568. }
  569.  
  570. /*    Function Name: VerticalScroll
  571.  *    Description: This function moves the postition of the interior window
  572.  *                   as the vertical scroll bar is moved.
  573.  *    Arguments: w - the scrollbar widget.
  574.  *                 junk - not used.
  575.  *                 pos - the position of the cursor.
  576.  *    Returns: none.
  577.  */
  578.  
  579. /* ARGSUSED */
  580. static void
  581. VerticalScroll(w,junk,pos)
  582. Widget w;
  583. caddr_t junk;
  584. int pos;
  585. {
  586.   int new_line;            /* The new location for the line pointer. */
  587.   ScrollByLineWidget sblw = (ScrollByLineWidget) XtParent(w);
  588.  
  589.   new_line = sblw->scroll.line_pointer + (pos / sblw->scroll.font_height);
  590.   (void) ScrollVerticalText( (Widget) sblw, new_line, FALSE);
  591.   SetThumb( (Widget) sblw);
  592. }
  593.  
  594. int h_width;            /* main font width */
  595.  
  596. /* ARGSUSED */
  597. static void 
  598. Initialize(req, new)
  599. Widget req, new;
  600. {
  601.   ScrollByLineWidget sblw = (ScrollByLineWidget) new;
  602.   unsigned long figWidth;
  603.   Atom atomNum;
  604.  
  605.   sblw->scroll.top_line = NULL;
  606.   sblw->scroll.line_pointer = 0;
  607.   LoadFile(new);
  608.   sblw->scroll.bar = (Widget) NULL;
  609.  
  610.   sblw->scroll.font_height = (sblw->scroll.normal_font->max_bounds.ascent + 
  611.                   sblw->scroll.normal_font->max_bounds.descent); 
  612.  
  613.   atomNum = XInternAtom(XtDisplay(req), "FIGURE_WIDTH", False);
  614.  
  615.   if (XGetFontProperty(sblw->scroll.normal_font, atomNum, &figWidth))
  616.       h_width = figWidth;
  617.   else
  618.     h_width = XTextWidth(sblw->scroll.normal_font, "$", 1);
  619.  
  620.  
  621.  
  622. } /* Initialize. */
  623.  
  624. /*    Function Name: CreateGCs
  625.  *    Description: Creates the graphics contexts that we need. 
  626.  *    Arguments: w - the sblw.
  627.  *    Returns: none
  628.  */
  629.  
  630. static void
  631. CreateGCs(w) 
  632. Widget w;
  633. {
  634.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  635.  
  636.   XtGCMask mask;
  637.   XGCValues values;
  638.  
  639.   values.graphics_exposures = TRUE;
  640.   sblw->scroll.move_gc = XtGetGC(w, GCGraphicsExposures, &values);
  641.  
  642.   mask = GCForeground | GCFont;
  643.   values.foreground = sblw->scroll.foreground;
  644.   
  645.   values.font = sblw->scroll.normal_font->fid;
  646.   sblw->scroll.normal_gc = XtGetGC(w, mask, &values);
  647.  
  648.   values.font = sblw->scroll.italic_font->fid;
  649.   sblw->scroll.italic_gc = XtGetGC(w, mask, &values);
  650.  
  651.   values.font = sblw->scroll.bold_font->fid;
  652.   sblw->scroll.bold_gc = XtGetGC(w, mask, &values);
  653.  
  654.   values.font = sblw->scroll.symbol_font->fid;
  655.   sblw->scroll.symbol_gc = XtGetGC(w, mask, &values);
  656. }
  657.  
  658. /*    Function Name: DestroyGCs
  659.  *    Description: removes all gcs for this widget.
  660.  *    Arguments: w - the widget.
  661.  *    Returns: none
  662.  */
  663.  
  664. static void
  665. DestroyGCs(w)
  666. Widget w;
  667. {
  668.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  669.  
  670.   XtReleaseGC(w, sblw->scroll.normal_gc);
  671.   XtReleaseGC(w, sblw->scroll.bold_gc);
  672.   XtReleaseGC(w, sblw->scroll.italic_gc);
  673.   XtReleaseGC(w, sblw->scroll.move_gc);
  674. }
  675.  
  676. static void
  677. Realize(w, valueMask, attributes)
  678. register Widget w;
  679. Mask *valueMask;
  680. XSetWindowAttributes *attributes;
  681. {
  682.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  683.  
  684.   CreateScrollbar(w);
  685.   CreateGCs(w);
  686.   Layout(w);
  687.   (*superclass->core_class.realize) (w, valueMask, attributes);
  688.   XtRealizeWidget(sblw->scroll.bar); /* realize scrollbar. */
  689.   XtMapWidget(sblw->scroll.bar); /* map scrollbar. */
  690.  
  691.   XtAddEventHandler(w, 0, TRUE, GExpose, NULL); /* Get Graphics Exposures */
  692. } /* Realize */
  693.  
  694. /*    Function Name: Destroy
  695.  *    Description: Cleans up when we are killed.
  696.  *    Arguments: w - the widget.
  697.  *    Returns: none
  698.  */
  699.  
  700. static void
  701. Destroy(w)
  702. Widget w;
  703. {
  704.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  705.  
  706.   if (sblw->scroll.bar != NULL)
  707.     XtDestroyWidget(sblw->scroll.bar); /* Destroy scrollbar. */
  708.   if (sblw->scroll.file != NULL)
  709.     fclose(sblw->scroll.file);
  710.   DestroyGCs(w);
  711. }
  712.  
  713. /*
  714.  *
  715.  * Set Values
  716.  *
  717.  */
  718.  
  719. /* ARGSUSED */
  720. static Boolean 
  721. SetValuesHook( w, args, num_args)
  722. Widget w;
  723. ArgList args;
  724. Cardinal *num_args;
  725. {
  726.   Boolean ret = TRUE;
  727.   int i;
  728.  
  729.   for (i = 0; i < *num_args; i++) {
  730.     if (strcmp(XtNfile, args[i].name) == 0) {
  731.       LoadFile(w);
  732.       ret = TRUE;
  733.     }
  734.   }
  735.  
  736. /*
  737.  * Changing anthing else will have strange effects, I don't need it so
  738.  * I didn't code it.
  739.  */
  740.  
  741.   return(ret);
  742.  
  743. } /* Set Values */
  744.  
  745. /* 
  746.  * A little design philosophy is probabally wise to include at this point.
  747.  *
  748.  * One of the things that I has hoped to achieve with xman is to make the
  749.  * viewing of manpage not only easy for the nieve user, but also fast for
  750.  * the experienced user, I wanted to be able to use it too.  To achieve this
  751.  * I end up sacrificing a bit of start up time for the manual data structure.
  752.  * As well as, the overhead of reading the entire file before putting it up 
  753.  * on the display.  This is actually hardly even noticeable since most manual
  754.  * pages are shots, one to two pages - the notable exception is of course csh,
  755.  * but then that should be broken up anyway. 
  756.  *
  757.  * METHOD:
  758.  *
  759.  * I allocate a chunk of space that is the size of the file, plus a null for
  760.  * debugging.  Then copiesthe file into this chunk of memory.  I then allocate
  761.  * an array of char*'s that are assigned to the beginning of each line.  Yes,
  762.  * this means that I have to read the file twice, and could probabally be more
  763.  * clever about it, but once it is in memory the second read is damn fast.
  764.  * There are a few obsucrities here about guessing the number of lines and
  765.  * reallocing if I guess wrong, but other than that it is pretty straight 
  766.  * forward.
  767.  *
  768.  *                                         Chris Peterson
  769.  *                                         1/27/88
  770.  */
  771.  
  772. #define ADD_MORE_MEM 100    /* first guesses for allocations. */
  773. #define CHAR_PER_LINE 40
  774.  
  775. /*    Function Name: LoadFile
  776.  *    Description: Loads the current file into the internal memory.
  777.  *    Arguments: w - the sblw.
  778.  *    Returns: none
  779.  */
  780.  
  781. static void
  782. LoadFile(w)
  783. Widget w;
  784. {
  785.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  786.   FILE * file = sblw->scroll.file;
  787.  
  788.   char *page;
  789.   char **line_pointer,**top_line; /* pointers to beginnings of the 
  790.                      lines of the file. */
  791.   int nlines;            /* the current number of allocated lines. */
  792.   struct stat fileinfo;        /* file information from fstat. */
  793.  
  794.   if ( sblw->scroll.top_line != NULL) {
  795.     XtFree(*(sblw->scroll.top_line)); /* free characters. */
  796.     XtFree((char *)(sblw->scroll.top_line)); /* free lines. */
  797.   }
  798.   sblw->scroll.top_line = NULL;
  799.  
  800.   if (file == NULL)
  801.     return;
  802.  
  803. /*
  804.  * Get file size and allocate a chunk of memory for the file to be 
  805.  * copied into.
  806.  */
  807.  
  808.   if (fstat(fileno(file), &fileinfo))
  809.     XtAppError(XtWidgetToApplicationContext(w), 
  810.            "SBLW LoadFile: Failure in fstat.");
  811.  
  812. /* 
  813.  * Allocate a space for a list of pointer to the beginning of each line.
  814.  */
  815.  
  816.   if ( (nlines = fileinfo.st_size/CHAR_PER_LINE) == 0)
  817.     return;
  818.  
  819.   page = XtMalloc(fileinfo.st_size + 1); /* leave space for the NULL */
  820.   top_line = line_pointer = (char**) XtMalloc( nlines * sizeof(char *) );
  821.  
  822. /*
  823.  * Copy the file into memory. 
  824.  */
  825.  
  826.   if (fread(page, sizeof(char), fileinfo.st_size, file) == 0)
  827.     XtAppError(XtWidgetToApplicationContext(w), 
  828.            "SBLW LoadFile: Failure in fread.");
  829.  
  830.  
  831. /* put NULL at end of buffer. */
  832.  
  833.   *(page + fileinfo.st_size) = '\0';
  834.  
  835. /*
  836.  * Go through the file setting a line pointer to the character after each 
  837.  * new line.  If we run out of line pointer space then realloc that space
  838.  * with space for more lines.
  839.  */
  840.  
  841.   *line_pointer++ = page;    /* first line points to first char in buffer.*/
  842.   while (*page != '\0') {
  843.  
  844.     if ( *page == '\n' ) {
  845.       *line_pointer++ = page + 1;
  846.  
  847.       if (line_pointer >= top_line + nlines) {
  848.     top_line = (char **) XtRealloc( (char *)top_line, (nlines +
  849.                       ADD_MORE_MEM) * sizeof(char *) );
  850.     line_pointer = top_line + nlines;
  851.     nlines += ADD_MORE_MEM;
  852.       }
  853.     }
  854.     page++;
  855.   }
  856.    
  857. /*
  858.  *  Realloc the line pointer space to take only the minimum amount of memory
  859.  */
  860.  
  861.   sblw->scroll.lines = nlines = line_pointer - top_line - 1;
  862.   top_line = (char **) XtRealloc((char *)top_line, nlines * sizeof(char *));
  863.  
  864. /*
  865.  * Store the memory pointers
  866.  */
  867.  
  868.   sblw->scroll.top_line = top_line;
  869.   sblw->scroll.line_pointer = 0;
  870.   SetThumbHeight(w);
  871.   SetThumb(w);
  872. }
  873.  
  874. #define NLINES  66        /* This is the number of lines to wait until
  875.                    we boldify the line again, this allows 
  876.                    me to bold the first line of each page.*/
  877. #define BACKSPACE 010        /* I doubt you would want to change this. */
  878.  
  879. #define NORMAL    0
  880. #define BOLD    1
  881. #define ITALIC    2
  882. #define SYMBOL    3
  883. #define WHICH(italic, bold)    ((bold) ? BOLD : ((italic) ? ITALIC : NORMAL))
  884.                 /* Choose BOLD over ITALICS.  If neither */
  885.                 /* is chosen, use NORMAL. */
  886.  
  887. static int DumpText();
  888. static Boolean Boldify();    
  889.  
  890. /*    Function Name: PrintText
  891.  *    Description: This function actually prints the text.
  892.  *    Arguments: w - the ScrollByLine widget.
  893.  *                 start_line - line to start printing,
  894.  *                 num_lines - the number of lines to print.
  895.  *                 location - the location to print the text.
  896.  *    Returns: none.
  897.  */
  898.  
  899. /* ARGSUSED */
  900.  
  901. static void
  902. PrintText(w, start_line, num_lines, location)
  903. Widget w;
  904. int  start_line, num_lines, location;
  905. {
  906.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  907.  
  908.   register char *bufp, *c;    /* Generic char pointers */
  909.   int current_line;        /* the number of the currrent line */
  910.   char buf[BUFSIZ];        /* Misc. characters */
  911.   int width;            /* Width of a tab stop. */
  912.   Boolean italicflag = FALSE;    /* Print text in italics?? */
  913.   Boolean first = TRUE;            /* First line of a manual page??? */
  914.   int x_loc, y_loc;        /* x and y location of text. */
  915.  
  916. /*
  917.  * For table hack
  918.  * To get columns to line up reasonably in most cases, make the
  919.  * assumption that they were lined up using lots of spaces, where
  920.  * lots is greater than two. Use a space width of 70% of the
  921.  * widest character in the font.
  922.  */
  923.   XFontStruct * h_font;
  924.   int h_col, h_fix;
  925.   char * h_c;
  926.  
  927.   h_font = sblw->scroll.normal_font;
  928.  
  929. /*
  930.  * Nothing loaded, take no action.
  931.  */
  932.  
  933.   if (sblw->scroll.top_line == NULL || num_lines == 0)
  934.     return;
  935.  
  936.   current_line = start_line;
  937.  
  938. /* Set the first character to print at the first line. */
  939.  
  940.   c = *(sblw->scroll.top_line + start_line);
  941.  
  942.  /* Width of a tab stop. */
  943.   width = 8 * h_width;
  944.  
  945. /*
  946.  * Because XDrawString uses the bottom of the text as a position
  947.  * reference, add the height from the top of the font to the baseline
  948.  * to the ScollByLine position reference.
  949.  */
  950.  
  951.   y_loc = location + sblw->scroll.normal_font->max_bounds.ascent;
  952.  
  953. /*
  954.  * Ok, here's the more than mildly heuristic man page formatter.
  955.  * We put chars into buf until either a font change or newline
  956.  * occurs (at which time we flush it to the screen.)
  957.  */
  958.  
  959.  
  960.   bufp = buf;
  961.   x_loc = sblw->scroll.offset + sblw->scroll.indent;
  962.   h_col = 0;
  963.   
  964. /*
  965.  * A fix:
  966.  * Assume that we are always starting to print on or before the
  967.  * first line of a page, and then prove it if we aren't.
  968.  */
  969.   for (h_fix = 1; h_fix <= (start_line % NLINES); h_fix++)
  970.     if (**(sblw->scroll.top_line + start_line - h_fix) != '\n')
  971.       {
  972.     first = FALSE;
  973.     break;
  974.       }
  975.  
  976.   while(TRUE) {
  977.     if (current_line % NLINES == 0)
  978.       first = TRUE;
  979.  
  980. /* 
  981.  * Lets make sure that we do not run out of space in our buffer, making full
  982.  * use of it is not critical since no window will be wide enough to display
  983.  * nearly BUFSIZ characters.
  984.  */
  985.  
  986.     if ( (bufp - buf) > (BUFSIZ - 10) )
  987.       /* Toss everything until we find a <CR> or the end of the buffer. */
  988.       while ( (*c != '\n') && (*c != '\0') ) c++;
  989.  
  990.     switch(*c) {
  991.  
  992.     case '\0':              /* If we reach the end of the file then return */
  993.       DumpText(w, x_loc, y_loc, buf, bufp - buf, WHICH(italicflag, first));
  994.       return;
  995.  
  996.     case '\n':
  997.       if (bufp != buf) {
  998.     Boolean bold;
  999.     *bufp = '\0';        /* for Boldify. */
  1000.     bold = ( (first) || ((x_loc == (sblw->scroll.offset +
  1001.                   sblw->scroll.indent)) && Boldify(buf)) );
  1002.  
  1003.     (void) DumpText(w, x_loc, y_loc, buf, bufp - buf,
  1004.             WHICH(italicflag, bold));
  1005.  
  1006.     if (bold)
  1007.       first = FALSE;
  1008.       }
  1009.  
  1010. /* 
  1011.  * If we have painted the required number of lines then we should now return.
  1012.  */
  1013.       if (++current_line == start_line + num_lines ) 
  1014.     return;
  1015.  
  1016.       bufp = buf;
  1017.       italicflag = FALSE;
  1018.       x_loc = sblw->scroll.offset + sblw->scroll.indent;
  1019.       h_col = 0;
  1020.       y_loc += sblw->scroll.font_height;
  1021.       break;
  1022.  
  1023. /*
  1024.  * This tab handling code is not very clever it moves the cursor over
  1025.  * to the next boundry of eight (8) spaces, as calculated in width just
  1026.  * before the printing loop started.
  1027.  */
  1028.  
  1029.     case '\t':            /* TAB */
  1030.       x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
  1031.                WHICH(italicflag, first));
  1032.       h_col += bufp - buf;
  1033.       bufp = buf; 
  1034.       italicflag = FALSE;
  1035.       x_loc = sblw->scroll.offset + sblw->scroll.indent;
  1036.       h_col = h_col + 8 - (h_col%8);
  1037.       x_loc += h_width * h_col;
  1038.       break;
  1039.  
  1040.     case ' ':
  1041.       h_c = c + 1;
  1042.       while (*h_c == ' ') h_c++;
  1043.  
  1044.       if (h_c - c < 4)
  1045.     {
  1046.       *bufp++ = *c;
  1047.       break;
  1048.     }
  1049.  
  1050.       x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
  1051.                WHICH(italicflag, first));
  1052.       h_col += bufp - buf;
  1053.       bufp = buf; 
  1054.       italicflag = FALSE;
  1055.  
  1056.       x_loc = sblw->scroll.offset + sblw->scroll.indent; 
  1057.       h_col += (h_c - c);
  1058.       x_loc += h_width * h_col; 
  1059.       c = h_c - 1;
  1060.       break;
  1061.  
  1062.     case '\033':        /* ignore esc sequences for now */
  1063.       c++;            /* should always be esc-x */
  1064.       break;
  1065.  
  1066. /* 
  1067.  * Overstrike code supplied by: cs.utexas.edu!ut-emx!clyde@rutgers.edu 
  1068.  * Since my manual pages do not have overstrike I couldn't test this.
  1069.  */
  1070.  
  1071.     case BACKSPACE:        /* Backspacing for nroff bolding */
  1072.       if (c[-1] == c[1] && c[1] != BACKSPACE) {    /* overstriking one char */
  1073.     bufp--;        /* Zap 1st instance of char to bolden */
  1074.     if (bufp > buf) {
  1075.       x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
  1076.                WHICH(italicflag, FALSE));
  1077.       h_col += bufp - buf;
  1078.       bufp = buf;
  1079.     }
  1080.     *bufp++ = c[1];
  1081.     x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf, BOLD);
  1082.     h_col += bufp - buf;
  1083.     bufp = buf;
  1084.     first = FALSE;
  1085.  
  1086.     /*
  1087.      *     Nroff bolding looks like:
  1088.      *              C\bC\bC\bCN...
  1089.      * c points to ----^      ^
  1090.      * it needs to point to --^
  1091.      */
  1092.     while (*c == BACKSPACE && c[-1] == c[1])
  1093.       c += 2;
  1094.     c--;        /* Back up to previous char */
  1095.       }
  1096.       else {
  1097.     if ((c[-1] == 'o' && c[1] == '+')          /* Nroff bullet */
  1098.         || (c[-1] == '+' && c[1] == 'o')) {       /* Nroff bullet */
  1099.                 /* If we run into a bullet, print out */
  1100.                 /* everything that's accumulated to this */
  1101.                 /* point, then the bullet, then resume. */
  1102.       bufp--;
  1103.       x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
  1104.                WHICH(italicflag, FALSE));
  1105.       h_col += bufp - buf;
  1106.       bufp = buf;
  1107.       *bufp = (char)183;
  1108.       x_loc = DumpText(w, x_loc, y_loc, buf, 1, SYMBOL);
  1109.       h_col++;
  1110.       c++;
  1111.     }
  1112.     else {        /* 'real' backspace - back up output ptr */
  1113.       bufp--;
  1114.     }
  1115.       }
  1116.       break;
  1117.  
  1118. /* End of contributed overstrike code. */
  1119.   
  1120.    case '_':            /* look for underlining [italicize] */
  1121.       if(*(c + 1) == BACKSPACE) {
  1122.     if(!italicflag) {    /* font change? */
  1123.       if (bufp != buf) {
  1124.         x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf, NORMAL);
  1125.         h_col += bufp - buf;
  1126.         bufp = buf;
  1127.       }
  1128.       italicflag = TRUE;
  1129.     }
  1130.     c += 2;
  1131.     *bufp++ = *c;
  1132.     break;
  1133.       }
  1134.       /* else fall through to default, because this was a real underscore. */
  1135.  
  1136.     default:
  1137.       if(italicflag) {             /* font change? */
  1138.     if (bufp != buf) {
  1139.       x_loc = DumpText(w, x_loc, y_loc, buf, bufp - buf,
  1140.                WHICH(italicflag, FALSE));
  1141.       h_col += bufp - buf;
  1142.       bufp = buf;
  1143.     }
  1144.     italicflag = FALSE;
  1145.       }
  1146.       *bufp++ = *c;
  1147.       break;
  1148.     }
  1149.     c++;
  1150.   }
  1151. }
  1152.  
  1153. /*    Function Name: DumpText
  1154.  *    Description: Dumps text to the screen.
  1155.  *    Arguments: w - the widget.
  1156.  *                 x_loc - to dump text at.
  1157.  *                 y_loc - the y_location to draw_text.
  1158.  *                 buf - buffer to dump.
  1159.  *                 italic, bold, boolean that tells us which gc to use.
  1160.  *    Returns: x_location of the end of the text.
  1161.  */
  1162.  
  1163. static int
  1164. DumpText(w, x_loc, y_loc, buf, len, format)
  1165. Widget w;
  1166. int x_loc, y_loc;
  1167. char * buf;
  1168. int len;
  1169. int format;
  1170. {
  1171.   ScrollByLineWidget sblw = (ScrollByLineWidget) w;
  1172.   GC gc;
  1173.   XFontStruct * font;
  1174.  
  1175.   switch(format) {
  1176.  
  1177.   case ITALIC:
  1178.     gc = sblw->scroll.italic_gc;
  1179.     font = sblw->scroll.italic_font;
  1180.     break;
  1181.  
  1182.   case BOLD:
  1183.       gc = sblw->scroll.bold_gc;
  1184.       font = sblw->scroll.bold_font;
  1185.     break;
  1186.  
  1187.   case SYMBOL:
  1188.     gc = sblw->scroll.symbol_gc;
  1189.     font = sblw->scroll.symbol_font;
  1190.     break;
  1191.  
  1192.   default:
  1193.       gc = sblw->scroll.normal_gc;
  1194.       font = sblw->scroll.normal_font;
  1195.     break;
  1196.     }
  1197.  
  1198.   XDrawString(XtDisplay(w), XtWindow(w), gc, x_loc, y_loc, buf, len);
  1199.   return(x_loc + XTextWidth(font, buf, len));
  1200. }
  1201.  
  1202. /*    Function Name: Boldify
  1203.  *    Description: look for keyword.
  1204.  *    Arguments: sp - string pointer.
  1205.  *    Returns: 1 if keyword else 0.
  1206.  */
  1207.  
  1208. static Boolean
  1209. Boldify(sp)
  1210. register char *sp;
  1211. {
  1212.   register char *sp_pointer;
  1213.   int length,count;
  1214.  
  1215. /* 
  1216.  * If there are not lower case letters in the line the assume it is a
  1217.  * keyword and boldify it in PrintManpage.
  1218.  */
  1219.  
  1220.   length = strlen(sp);
  1221.   for (sp_pointer = sp, count = 0; count < length; sp_pointer++,count++) 
  1222.     if ( !isupper(*sp_pointer) && !isspace(*sp_pointer) )
  1223.       return(0);
  1224.   return(1);
  1225. }
  1226.  
  1227. #undef superclass
  1228.